unit RclFile;

interface

uses
  Windows, Classes,
  JclFileUtils;

type
  TFileAttribute = (faArchive, faDirectory, faHidden, faReadOnly, faSysFile);
  TFileAttributes = set of TFileAttribute;

  TRclFileUtils = class(TObject)
  private
    class function GetAttributes(const FileName: String; out Attributes:
        TFileAttributes): Boolean;
    class function WindowsAttrToAttributes(Attr: Integer): TFileAttributes;
  public
    class function ChangeFileExt(const FileName, Extension: string): string;
    class function CopyFile(const Source, Dest: String): Boolean;
    class function CreateDirectory(const PathName: String): Boolean;
    class function DateTimeToFileTime(const DateTime: TDateTime): TFileTime;
    class function DeleteFile(const FileName: String): Boolean;
    class function DelimiterCanonicalize(APath: string): string;
    class function DirectoryExists(Path: string): Boolean;
    class function Exists(FileName: string): Boolean;
    class function FileTimeToDateTime(const FileTime: TFileTime): TDateTime;
    class function GetApplicationDir: string;
    class function GetApplicationPath: string;
    class function GetFileExt(const FileName: String; IncludeDot: Boolean = true):
        String;
    class function GetFileName(const FileName: String; IncludeExt: Boolean = true):
        String;
    class function GetFilePath(const FileName: String): String;
    class function GetFileSize(Handle: THandle; out Size: LongWord): Boolean;
    class function GetFileTime(Handle: THandle; out Time: TDateTime): Boolean;
    class function IncludeSlash(FileName: string): string;
    class function IsDirectory(const FileName: string): Boolean;
    class function IsFile(const FileName: string): Boolean;
    class procedure ListFiles(Path: string; List: TStrings);
    class function MoveFile(const OldName, NewName: String): Boolean;
    class function PathCanonicalize(const Path: string): string;
    class function RemoveDirectory(const PathName: String): Boolean;
    class function Separator: string;
    class function SetFileTime(Handle: THandle; const Time: TDateTime): Boolean;
    class function ToAbsolutePath(Path: string): string;
    class function TranslatePath(const Path: String; Old, New: Char): String;
  end;

implementation

uses
  SysUtils, Forms,
  RclStr;

const
  FileAttributes: array [TFileAttribute] of Integer = (
    FILE_ATTRIBUTE_ARCHIVE, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_HIDDEN,
    FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_SYSTEM);

class function TRclFileUtils.ChangeFileExt(const FileName, Extension: string):
    string;
var
  I: Integer;
begin
  I := SysUtils.LastDelimiter('.\:', FileName);
  if (I = 0) or (FileName[I] <> '.') then I := MaxInt;

  Result := Copy(FileName, 1, I - 1);

  if (Length(Extension) > 0) then
  begin
    if (Extension[1] <> '.') then
      Str.Add(Result, '.');
    Str.Add(Result, Extension);
  end;
end;

class function TRclFileUtils.CopyFile(const Source, Dest: String): Boolean;
begin
  Result := Windows.CopyFile(PChar(Source), PChar(Dest), false);
end;

class function TRclFileUtils.CreateDirectory(const PathName: String): Boolean;
begin
  Result := Windows.CreateDirectory(PChar(PathName), nil);
end;

class function TRclFileUtils.DateTimeToFileTime(const DateTime: TDateTime):
    TFileTime;
var
  FTime: TFileTime;
  STime: TSystemTime;
begin
  DateTimeToSystemTime(DateTime, STime);
  if not Windows.SystemTimeToFileTime(STime, FTime) or not Windows.LocalFileTimeToFileTime(FTime, Result) then
    FillChar(Result, SizeOf(TDateTime), 0);
end;

class function TRclFileUtils.DeleteFile(const FileName: String): Boolean;
begin
  Result := Windows.DeleteFile(PChar(FileName));
end;

class function TRclFileUtils.DelimiterCanonicalize(APath: string): string;
begin
  Result := APath;
  Str.Replace(Result, '/', '\');
end;

class function TRclFileUtils.DirectoryExists(Path: string): Boolean;
var
  Attr: TFileAttributes;
begin
  Result := GetAttributes(Path, Attr) and (faDirectory in Attr);
end;

class function TRclFileUtils.Exists(FileName: string): Boolean;
begin
  Result := FileExists(FileName);
end;

class function TRclFileUtils.FileTimeToDateTime(const FileTime: TFileTime):
    TDateTime;
var
  LocalTime: TFileTime;
  SystemTime: TSystemTime;
begin
  if Windows.FileTimeToLocalFileTime(FileTime, LocalTime) and
    Windows.FileTimeToSystemTime(LocalTime, SystemTime) then
    Result := SystemTimeToDateTime(SystemTime) else
    Result := 0;
end;

class function TRclFileUtils.GetApplicationDir: string;
begin
  Result := ExtractFileDir(Application.ExeName);
end;

class function TRclFileUtils.GetApplicationPath: string;
begin
  Result := ExtractFilePath(Application.ExeName);
end;

class function TRclFileUtils.GetAttributes(const FileName: String; out Attributes:
    TFileAttributes): Boolean;
var
  Attr: DWORD;
  OldMode: LongWord;
begin
  OldMode := Windows.SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
  try
    Attr := Windows.GetFileAttributes(PChar(FileName));
    Result := Attr <> $FFFFFFFF;
    if Result then Attributes := WindowsAttrToAttributes(Attr);
  finally
    Windows.SetErrorMode(OldMode);
  end;
end;

class function TRclFileUtils.GetFileExt(const FileName: String; IncludeDot: Boolean
    = true): String;
var
  I: Integer;
begin
  I := SysUtils.LastDelimiter('.\:', FileName);

  if (I > 0) and (FileName[I] = '.') then
    Result := Copy(FileName, I + Ord(not IncludeDot), MaxInt) else
    Result := '';
end;

class function TRclFileUtils.GetFileName(const FileName: String; IncludeExt: Boolean
    = true): String;
var
  I: Integer;
begin
  I := SysUtils.LastDelimiter('\:', FileName);
  Result := Copy(FileName, I + 1, MaxInt);

  if not IncludeExt then
  begin
    I := SysUtils.LastDelimiter('.\:', Result);
    if (I > 0) and (Result[I] = '.') then
      Result := Copy(Result, 1, I - 1);
  end;
end;

class function TRclFileUtils.GetFilePath(const FileName: String): String;
var
  I: Integer;
begin
  I := SysUtils.LastDelimiter('\:', FileName);
  Result := Copy(FileName, 1, I);
end;

class function TRclFileUtils.GetFileSize(Handle: THandle; out Size: LongWord):
    Boolean;
begin
  Size := Windows.GetFileSize(Handle, nil);
  Result := Size <> $FFFFFFFF;
end;

class function TRclFileUtils.GetFileTime(Handle: THandle; out Time: TDateTime):
    Boolean;
var
  dtTime: TFileTime;
begin
  Result := Windows.GetFileTime(Handle, nil, nil, @dtTime);
  if Result then Time := FileTimeToDateTime(dtTime);
end;

class function TRclFileUtils.IncludeSlash(FileName: string): string;
begin
  if (FileName[Length(FileName)] = '/') or (FileName[Length(FileName)] = '\') then
    SetLength(FileName, Length(FileName) - 1);
  Result := IncludeTrailingPathDelimiter(FileName);
end;

class function TRclFileUtils.IsDirectory(const FileName: string): Boolean;
begin
  Result := JclFileUtils.IsDirectory(FileName); 
end;

class function TRclFileUtils.IsFile(const FileName: string): Boolean;
var
  Attr: TFileAttributes;
begin
  Result := GetAttributes(FileName, Attr) and
      (([faArchive, faHidden, faReadOnly, faSysFile] * Attr) <> []);
end;

class procedure TRclFileUtils.ListFiles(Path: string; List: TStrings);
var
  SearchRec: TSearchRec;
  AbsPath: string;
begin
  AbsPath := ToAbsolutePath(Path);
  List.BeginUpdate;
  try
    FindFirst(AbsPath + '*', faAnyFile, SearchRec);
    repeat
      if (SearchRec.Name <> '.') and (SearchRec.Name <> '..') then
        List.Append(AbsPath + SearchRec.Name);
    until FindNext(SearchRec) <> 0;
    FindClose(SearchRec);
  finally
    List.EndUpdate;
  end;
end;

class function TRclFileUtils.MoveFile(const OldName, NewName: String): Boolean;
begin
  Result := Windows.MoveFileEx(PChar(OldName), PChar(NewName), MOVEFILE_REPLACE_EXISTING or MOVEFILE_COPY_ALLOWED or MOVEFILE_WRITE_THROUGH);
end;

class function TRclFileUtils.PathCanonicalize(const Path: string): string;
begin
  Result := JclFileUtils.PathCanonicalize(Path);
end;

class function TRclFileUtils.RemoveDirectory(const PathName: String): Boolean;
begin
  Result := Windows.RemoveDirectory(PChar(PathName));
end;

class function TRclFileUtils.Separator: string;
begin
  Result := JclFileUtils.DirDelimiter;
end;

class function TRclFileUtils.SetFileTime(Handle: THandle; const Time: TDateTime):
    Boolean;
var
  dtTime: TFileTime;
begin
  dtTime := DateTimeToFileTime(Time);

  if (dtTime.dwLowDateTime <> 0) and (dtTime.dwHighDateTime <> 0) then
    Result := Windows.SetFileTime(Handle, nil, nil, @dtTime) else
    Result := false;
end;

class function TRclFileUtils.ToAbsolutePath(Path: string): string;
begin
  if not JclFileUtils.PathIsAbsolute(Path) then
    Result := IncludeSlash(JclFileUtils.PathCanonicalize(GetApplicationPath + Path))
  else
    Result := IncludeSlash(Path);
end;

class function TRclFileUtils.TranslatePath(const Path: String; Old, New: Char):
    String;
var
  i: Integer;
begin
  Result := Path;
  if Str.IsEmpty(Result) then Exit;

  if (Result[1] = Old) and (Length(Result) > 1) and ((Length(Result) = 2) or (Result[3] = Old)) then
    Result := Result[2] + ':';

  for i := 1 to Length(Result) do
    if Result[i] = Old then
      Result[i] := New;
end;

class function TRclFileUtils.WindowsAttrToAttributes(Attr: Integer): TFileAttributes;
var
  i: TFileAttribute;
begin
  Result := [];

  for i := Low(FileAttributes) to High(FileAttributes) do
    if FileAttributes[i] and Attr > 0 then
      Include(Result, i);
end;

end.
